package io.github.mengflyl.excel.report.viewer;

import cn.hutool.core.util.StrUtil;
import io.github.mengfly.excel.report.Container;
import io.github.mengfly.excel.report.component.TextComponent;
import io.github.mengfly.excel.report.component.split.PageColSplitComponent;
import io.github.mengfly.excel.report.component.split.PageRowSplitComponent;
import io.github.mengfly.excel.report.entity.Size;
import io.github.mengfly.excel.report.layout.HLayout;
import io.github.mengfly.excel.report.layout.VLayout;
import lombok.Getter;
import lombok.Setter;

import javax.swing.*;
import java.awt.*;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.util.List;
import java.util.Objects;
import java.util.function.Consumer;

@Getter
public class ContainerViewer extends JComponent {
    public static final double maxScale = 10;
    public static final double minScale = 0.05;

    public static final int xSpan = 50;
    public static final int ySpan = 50;

    private Container container;
    private Container selectedContainer;
    private int selectContainerX;
    private int selectContainerY;
    @Setter
    private Consumer<Container> onContainerSelected;

    private boolean showGridLine = false;
    private boolean showCellNumber = true;

    private double scale = 1.0f;
    private double unitWidth = 100;
    private double unitHeight = 30;

    public ContainerViewer() {
        addMouseListener(new MouseAdapter() {
            @Override
            public void mouseClicked(MouseEvent e) {
                Container searchContainer;
                if (e.getX() - xSpan < 0 || e.getY() - ySpan < 0) {
                    searchContainer = container;
                } else {
                    int xIndex = (e.getX() - xSpan) / getScaleUnitWidth();
                    int yIndex = (e.getY() - ySpan) / getScaleUnitHeight();
                    searchContainer = searchContainer(container, 0, 0, xIndex, yIndex);
                }
                if (searchContainer != null) {
                    if (onContainerSelected != null) {
                        onContainerSelected.accept(searchContainer);
                    }
                    selectedContainer = searchContainer;
                    repaint();
                }
            }
        });
    }

    private int getScaleUnitWidth() {
        return (int) (unitWidth * scale);
    }

    private int getScaleUnitHeight() {
        return (int) (unitHeight * scale);
    }

    public void setScale(double scale) {
        this.scale = scale;
        repaint();
    }

    public void setUnitWidth(double width) {
        this.unitWidth = width;
        repaint();
    }

    public void setUnitHeight(double height) {
        this.unitHeight = height;
        repaint();
    }

    private Container searchContainer(Container container, int startX, int startY, int xIndex, int yIndex) {
        if (container == null) {
            return null;
        }

        if (container instanceof HLayout) {
            final List<Container> containers = ((HLayout) container).getContainers();
            int x = startX;

            for (Container childContainer : containers) {
                if (isInContainer(childContainer, x, startY, xIndex, yIndex)) {
                    return searchContainer(childContainer, x, startY, xIndex, yIndex);
                }
                x += childContainer.getSize().width;
            }
            return container;

        } else if (container instanceof VLayout) {
            final List<Container> containers = ((VLayout) container).getContainers();
            int y = startY;
            for (Container childContainer : containers) {
                if (isInContainer(childContainer, startX, y, xIndex, yIndex)) {
                    return searchContainer(childContainer, startX, y, xIndex, yIndex);
                }
                y += childContainer.getSize().height;
            }
            return container;
        } else {
            return container;
        }

    }

    private boolean isInContainer(Container container, int startX, int startY, int xIndex, int yIndex) {
        return xIndex >= startX && yIndex >= startY
                && xIndex < container.getSize().width + startX
                && yIndex < container.getSize().height + startY;
    }

    public void setContainer(Container container) {
        this.container = container;
        this.selectedContainer = null;
        updateUI();
    }

    public void setShowGridLine(boolean showGridLine) {
        this.showGridLine = showGridLine;
        updateUI();
    }

    public void setShowCellNumber(boolean showCellNumber) {
        this.showCellNumber = showCellNumber;
        updateUI();
    }

    @Override
    public int getWidth() {
        if (container == null) {
            return 400;
        }
        return (container.getSize().width) * getScaleUnitWidth() + xSpan + 10;
    }

    @Override
    public int getHeight() {
        if (container == null) {
            return 400;
        }
        return (container.getSize().height) * getScaleUnitHeight() + ySpan + 10;
    }

    @Override
    public Dimension getPreferredSize() {
        return new Dimension(getWidth(), getHeight());
    }

    @Override
    public void paint(Graphics g) {
        //super.paint(g);
        if (container == null) {
            g.drawString("拖动模板文件到此位置，或双击左侧RecentFile", 50, 50);
            return;
        }
        final int unitWidth = getScaleUnitWidth();
        final int unitHeight = getScaleUnitHeight();

        final Size size = container.getSize();

        Color color = g.getColor();
        g.setColor(Color.WHITE);
        g.clearRect(0, 0, getWidth(), getHeight());
        g.setColor(color);

        if (showGridLine) {
            color = g.getColor();
            g.setColor(Color.lightGray);
            for (int i = 0; i < size.width; i++) {
                g.drawLine(xSpan + i * unitWidth, ySpan, xSpan + i * unitWidth, ySpan + size.height * unitHeight);
            }
            // drawRow
            for (int i = 0; i < size.height; i++) {
                g.drawLine(xSpan, ySpan + i * unitHeight, xSpan + size.width * unitWidth, ySpan + i * unitHeight);
            }
            g.setColor(color);
        }
        paint(g, container, xSpan, ySpan);

        // drawSelect
        if (selectedContainer != null) {
            color = g.getColor();
            g.setColor(Color.RED);
            final Size selectSize = selectedContainer.getSize();
            g.drawRect(selectContainerX, selectContainerY, selectSize.width * unitWidth, selectSize.height * unitHeight);
            g.setColor(color);
        }

        if (showCellNumber) {
            // draw Row column
            for (int i = 0; i < size.width; i++) {
                g.drawString(String.format("%02d", i + 1), xSpan + i * unitWidth + 5, ySpan - 5);
            }
            // drawRow
            for (int i = 0; i < size.height; i++) {
                final String format = String.format("%02d", i + 1);
                g.drawString(format, xSpan - 10 * format.length(), ySpan + 15 + i * unitHeight);
            }
        }


    }

    public void paint(Graphics g, Container container, int x, int y) {
        final int unitWidth = getScaleUnitWidth();
        final int unitHeight = getScaleUnitHeight();

        final int width = container.getSize().width;
        if (container instanceof HLayout) {
            int curX = x;
            for (Container child : ((HLayout) container).getContainers()) {
                paint(g, child, curX, y);
                curX += child.getSize().width * unitWidth;
            }

        } else if (container instanceof VLayout) {
            int curY = y;
            for (Container child : ((VLayout) container).getContainers()) {
                paint(g, child, x, curY);
                curY += child.getSize().height * unitHeight;
            }
        } else if (container instanceof PageRowSplitComponent || container instanceof PageColSplitComponent) {
            return;
        } else {
            g.drawRect(x, y, width * unitWidth, container.getSize().height * unitHeight);
            if (scale >= 0.5) {
                String showText;
                if (container instanceof TextComponent) {
                    showText = Objects.toString(((TextComponent) container).getText());
                } else {
                    showText = container.getClass().getSimpleName();
                }
                showText = StrUtil.brief(showText, (int) (width * 7 * scale));

                g.drawString(showText, x + 10, y + 20);
            }
        }
        if (selectedContainer == container) {
            selectContainerX = x;
            selectContainerY = y;
        }
        g.drawRect(x, y, width * unitWidth, container.getSize().height * unitHeight);
    }

}
